home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 February / EnigmA AMIGA RUN 15 (1997)(G.R. Edizioni)(IT)[!][issue 1997-02][PLANET CD V].iso / enigma / earcd / sviluppo / svilupp2 / gmsppr10.lha / Score.c < prev    next >
C/C++ Source or Header  |  1996-09-27  |  28KB  |  1,165 lines

  1. #include <proto/alib.h>
  2. #include <proto/exec.h>
  3. #include <proto/dos.h>
  4. #include <proto/utility.h>
  5. #include <proto/iffparse.h>
  6. #include <proto/intuition.h>
  7.  
  8. #include "Global.h"
  9.  
  10. /************************************************************************/
  11.  
  12. struct ScoreHandle
  13. {
  14.   const struct GS_ScoreDef *ScoreDef;
  15.   const char *UserName;
  16.   BPTR DirLock;
  17.   struct
  18.     {
  19.       struct DateStamp LastChanged;    /* the timestamp that was valid when we read Scores */
  20.       struct GS_ScoreList Scores;    /* current file contents */
  21.       char *Filename;            /* relative to DirLock */
  22.     } FileData[3];
  23. };
  24.  
  25. /************************************************************************/
  26.  
  27. static char LockFilename[]=".lock.scores";
  28. static char TempFilename[]=".temp.scores";
  29.  
  30. /****** gamesupport.library/GS_ObtainScoreHandle *************************
  31. *
  32. *   NAME
  33. *    GS_ObtainScoreHandle -- obtain a handle to access score files
  34. *
  35. *   SYNOPSIS
  36. *    ScoreHandle = GS_ObtainScoreHandle(ScoreDef, SubPath, UserName)
  37. *         d0                              a0        a1        a2
  38. *
  39. *    void *GS_ObtainScoreHandle(const struct GS_ScoreDef *, const char *,
  40. *                               const char *);
  41. *
  42. *   FUNCTION
  43. *    Get a handle for later access to the score files
  44. *
  45. *   INPUTS
  46. *    ScoreDef    - an initialized GS_ScoreDef structure.
  47. *    SubPath     - an optional path, which can be used if the
  48. *                  game keeps several score files.
  49. *    UserName    - the user name. NULL or "" specifies the
  50. *                  generic ("unknown") user.
  51. *
  52. *   RESULT
  53. *    ScoreHandle - a handle to use with the other score functions.
  54. *                  NULL for failure.
  55. *
  56. *   NOTE
  57. *    UserName and ScoreDef must remain valid and unchanged for the
  58. *    lifespan of the handle.
  59. *
  60. *   NOTE
  61. *    The current search path is
  62. *        GAMESTUFF:<GameName> scores/<SubPath/>
  63. *        <GameName>:<GameName> scores/<SubPath/>
  64. *        PROGDIR:<GameName> scores/<SubPath/>
  65. *
  66. *************************************************************************/
  67.  
  68. SAVEDS_ASM_A0A1A2(void *,LibGS_ObtainScoreHandle,const struct GS_ScoreDef *,ScoreDef,const char *,SubPath,const char *,UserName)
  69.  
  70. {
  71.   struct ScoreHandle *ScoreHandle;
  72.   char *ThePath;
  73.   LONG Error;
  74.  
  75.   Error=0;
  76.   ScoreHandle=NULL;
  77.   if (UserName!=NULL && *UserName=='\0')
  78.     {
  79.       UserName=NULL;
  80.     }
  81.  
  82.   /* construct the relative path string */
  83.   {
  84.     const char *Params[2];
  85.  
  86.     Params[0]=ScoreDef->GameName;
  87.     Params[1]=SubPath;
  88.     ThePath=GS_FormatString("%s scores/%s",Params,NULL,NULL);
  89.   }
  90.  
  91.   if (ThePath!=NULL)
  92.     {
  93.       APTR WindowPtr;
  94.       int MissingLevels;
  95.  
  96.       WindowPtr=((struct Process *)FindTask(NULL))->pr_WindowPtr;
  97.       MissingLevels=0;
  98.       do
  99.     {
  100.       int DirNumber;
  101.  
  102.       for (DirNumber=0; ScoreHandle==NULL && Error==0 && DirNumber<3; DirNumber++)
  103.         {
  104.           char *Path;
  105.  
  106.           if (DirNumber==0)
  107.         {
  108.           Path=GS_FormatString("GAMESTUFF:%s",&ThePath,NULL,NULL);
  109.         }
  110.           else if (DirNumber==1)
  111.         {
  112.           const char *Params[2];
  113.  
  114.           Params[0]=ScoreDef->GameName;
  115.           Params[1]=ThePath;
  116.           Path=GS_FormatString("%s:%s",Params,NULL,NULL);
  117.         }
  118.           else
  119.         {
  120.           Path=GS_FormatString("PROGDIR:%s",&ThePath,NULL,NULL);
  121.         }
  122.           if (Path!=NULL)
  123.         {
  124.           BPTR PathLock;
  125.  
  126.           ((struct Process *)FindTask(NULL))->pr_WindowPtr=(APTR)-1;
  127.           while (Error==0 && (PathLock=Lock(Path,SHARED_LOCK))==MKBADDR(NULL))
  128.             {
  129.               LONG Err;
  130.  
  131.               Err=IoErr();
  132.               if (Err==ERROR_OBJECT_IN_USE)
  133.             {
  134.               Delay(TICKS_PER_SECOND);
  135.             }
  136.               else if (Err==ERROR_OBJECT_NOT_FOUND || Err==ERROR_DIR_NOT_FOUND || Err==ERROR_DEVICE_NOT_MOUNTED)
  137.             {
  138.               break;
  139.             }
  140.               else
  141.             {
  142.               Error=Err;
  143.             }
  144.             }
  145.           if (PathLock!=MKBADDR(NULL))
  146.             {
  147.               struct InfoData ALIGN(InfoData);
  148.  
  149.               ((struct Process *)FindTask(NULL))->pr_WindowPtr=WindowPtr;
  150.               if (Info(PathLock,&InfoData))
  151.             {
  152.               if (InfoData.id_DiskState==ID_WRITE_PROTECTED)
  153.                 {
  154.                   UnLock(PathLock);
  155.                   PathLock=MKBADDR(NULL);
  156.                 }
  157.             }
  158.               else
  159.             {
  160.               Error=IoErr();
  161.             }
  162.             }
  163.           if (PathLock!=MKBADDR(NULL))
  164.             {
  165.               while (Error==0 && MissingLevels>0)
  166.             {
  167.               BPTR DirLock;
  168.               char *NewDir;
  169.  
  170.               if (ThePath[0]=='\0')
  171.                 {
  172.                   ThePath[0]=ScoreDef->GameName[0];
  173.                   NewDir=ThePath;
  174.                 }
  175.               else
  176.                 {
  177.                   NewDir=ThePath+strlen(ThePath);
  178.                   assert(NewDir[0]=='\0');
  179.                   *(NewDir++)='/';
  180.                 }
  181.               PathLock=CurrentDir(PathLock);
  182.               if ((DirLock=CreateDir(NewDir))!=MKBADDR(NULL))
  183.                 {
  184.                   if (!ChangeMode(CHANGE_LOCK,DirLock,SHARED_LOCK))
  185.                 {
  186.                   LONG Err;
  187.  
  188.                   Err=IoErr();
  189.                   if (Err==ERROR_ACTION_NOT_KNOWN)
  190.                     {
  191.                       UnLock(DirLock);
  192.                       if ((DirLock=Lock(NewDir,SHARED_LOCK))==MKBADDR(NULL))
  193.                     {
  194.                       Error=IoErr();
  195.                     }
  196.                     }
  197.                   else
  198.                     {
  199.                       Error=Err;
  200.                     }
  201.                 }
  202.                   assert(DirLock!=MKBADDR(NULL) || Error!=0);
  203.                   if (Error!=0 && DirLock!=MKBADDR(NULL))
  204.                 {
  205.                   UnLock(DirLock);
  206.                   DirLock=MKBADDR(NULL);
  207.                 }
  208.                 }
  209.               else
  210.                 {
  211.                   LONG Err;
  212.  
  213.                   Err=IoErr();
  214.                   if (Err==ERROR_OBJECT_EXISTS)
  215.                 {
  216.                   while ((DirLock=Lock(NewDir,SHARED_LOCK))==MKBADDR(NULL) &&
  217.                      (Err=IoErr())==ERROR_OBJECT_IN_USE)
  218.                     {
  219.                       Delay(TICKS_PER_SECOND);
  220.                     }
  221.                 }
  222.                 }
  223.               PathLock=CurrentDir(PathLock);
  224.               assert((DirLock!=MKBADDR(NULL) && Error==0) || (DirLock==MKBADDR(NULL) && Error!=0));
  225.               if (DirLock!=MKBADDR(NULL))
  226.                 {
  227.                   UnLock(PathLock);
  228.                   PathLock=DirLock;
  229.                   MissingLevels--;
  230.                 }
  231.             }
  232.               assert(PathLock!=MKBADDR(NULL));
  233.               if (Error==0)
  234.             {
  235.               BPTR DirLock;
  236.  
  237.               PathLock=CurrentDir(PathLock);
  238.               if ((DirLock=CreateDir("Personal scores"))==MKBADDR(NULL))
  239.                 {
  240.                   LONG Err;
  241.  
  242.                   Err=IoErr();
  243.                   if (Err!=ERROR_OBJECT_EXISTS)
  244.                 {
  245.                   Error=Err;
  246.                 }
  247.                 }
  248.               else
  249.                 {
  250.                   UnLock(DirLock);
  251.                 }
  252.               PathLock=CurrentDir(PathLock);
  253.               if ((ScoreHandle=GS_MemoryAlloc(sizeof(*ScoreHandle)))!=NULL)
  254.                 {
  255.                   int i;
  256.  
  257.                   ScoreHandle->ScoreDef=ScoreDef;
  258.                   ScoreHandle->UserName=UserName;
  259.                   ScoreHandle->DirLock=PathLock;
  260.                   for (i=0; Error==0 && i<3; i++)
  261.                 {
  262.                   ScoreHandle->FileData[i].LastChanged.ds_Days=0;
  263.                   ScoreHandle->FileData[i].LastChanged.ds_Minute=0;
  264.                   ScoreHandle->FileData[i].LastChanged.ds_Tick=0;
  265.                   NewList((struct List *)&ScoreHandle->FileData[i].Scores.List);
  266.                   ScoreHandle->FileData[i].Scores.Count=0;
  267.                   switch (i)
  268.                     {
  269.                     case GS_SCORE_ROLL:
  270.                       ScoreHandle->FileData[i].Filename="Roll of honour";
  271.                       break;
  272.  
  273.                     case GS_SCORE_TODAY:
  274.                       ScoreHandle->FileData[i].Filename="Todays contenders";
  275.                       break;
  276.  
  277.                     case GS_SCORE_PERSONAL:
  278.                       if (UserName!=NULL)
  279.                     {
  280.                       char *Filename;
  281.  
  282.                       if ((Filename=GS_TransformUsername(UserName,NULL))!=NULL)
  283.                         {
  284.                           if ((ScoreHandle->FileData[i].Filename=GS_FormatString("Personal scores/%s",&Filename,NULL,NULL))==NULL)
  285.                         {
  286.                           Error=ERROR_NO_FREE_STORE;
  287.                         }
  288.                           GS_MemoryFree(Filename);
  289.                         }
  290.                       else
  291.                         {
  292.                           Error=ERROR_NO_FREE_STORE;
  293.                         }
  294.                     }
  295.                       else
  296.                     {
  297.                       ScoreHandle->FileData[i].Filename=NULL;
  298.                     }
  299.                     }
  300.                 }
  301.                   if (Error==0)
  302.                 {
  303.                   PathLock=MKBADDR(NULL);
  304.                 }
  305.                   else
  306.                 {
  307.                   GS_MemoryFree(ScoreHandle);
  308.                 }
  309.                 }
  310.               else
  311.                 {
  312.                   Error=ERROR_NO_FREE_STORE;
  313.                 }
  314.             }
  315.               if (PathLock!=MKBADDR(NULL))
  316.             {
  317.               UnLock(PathLock);
  318.             }
  319.             }
  320.         }
  321.           else
  322.         {
  323.           Error=ERROR_NO_FREE_STORE;
  324.         }
  325.           GS_MemoryFree(Path);
  326.         }
  327.       if (ScoreHandle==NULL && Error==0)
  328.         {
  329.           if (ThePath[0]=='\0')
  330.         {
  331.           Error=-1;
  332.         }
  333.           else
  334.         {
  335.           char *t;
  336.  
  337.           t=PathPart(ThePath);
  338.           *t='\0';
  339.           MissingLevels++;
  340.         }
  341.         }
  342.     }
  343.       while (Error==0 && ScoreHandle==NULL);
  344.       ((struct Process *)FindTask(NULL))->pr_WindowPtr=WindowPtr;
  345.       GS_MemoryFree(ThePath);
  346.     }
  347.   else
  348.     {
  349.       Error=ERROR_NO_FREE_STORE;
  350.     }
  351.   SetIoErr(Error);
  352.   return ScoreHandle;
  353. }
  354.  
  355. /************************************************************************/
  356. /*                                    */
  357. /* Free a score list                            */
  358. /*                                    */
  359. /************************************************************************/
  360.  
  361. static void FreeScores(struct GS_ScoreList *Scores)
  362.  
  363. {
  364.   struct GS_Score *Score;
  365.  
  366.   while ((Score=(struct GS_Score *)RemHead((struct List *)&Scores->List))!=NULL)
  367.     {
  368.       GS_MemoryFree(Score);
  369.       Scores->Count--;
  370.     }
  371.   assert(Scores->Count==0);
  372. }
  373.  
  374. /****** gamesupport.library/GS_ReleaseScoreHandle ************************
  375. *
  376. *   NAME
  377. *    GS_ReleaseScoreHandle -- release a score handle
  378. *
  379. *   SYNOPSIS
  380. *    GS_ReleaseScoreHandle(ScoreHandle)
  381. *                               a0
  382. *
  383. *    void GS_ReleaseScoreHandle(void *);
  384. *
  385. *   FUNCTION
  386. *    Release a handle obtained from GS_ObtainScoreHandle().
  387. *
  388. *   INPUTS
  389. *    ScoreHandle - the score handle to release, or NULL
  390. *
  391. *************************************************************************/
  392.  
  393. SAVEDS_ASM_A0(void,LibGS_ReleaseScoreHandle,struct ScoreHandle *,ScoreHandle)
  394.  
  395. {
  396.   if (ScoreHandle!=NULL)
  397.     {
  398.       ULONG i;
  399.  
  400.       UnLock(ScoreHandle->DirLock);
  401.       for (i=0; i<ARRAYSIZE(ScoreHandle->FileData); i++)
  402.     {
  403.       FreeScores(&ScoreHandle->FileData[i].Scores);
  404.     }
  405.       GS_MemoryFree(ScoreHandle->FileData[2].Filename);
  406.       GS_MemoryFree(ScoreHandle);
  407.     }
  408. }
  409.  
  410. /************************************************************************/
  411. /*                                    */
  412. /*                                    */
  413. /************************************************************************/
  414.  
  415. static int MyStrcmp(const char *String1, const char *String2)
  416.  
  417. {
  418.   if (String1==NULL)
  419.     {
  420.       String1="";
  421.     }
  422.   if (String2==NULL)
  423.     {
  424.       String2="";
  425.     }
  426.   return strcmp(String1,String2);
  427. }
  428.  
  429. /************************************************************************/
  430. /*                                    */
  431. /* Insert a score into a score list.                    */
  432. /* If it returns non-NULL, the returned score fell out of the table.    */
  433. /*                                    */
  434. /************************************************************************/
  435.  
  436. static struct GS_Score *InsertScore(struct ScoreHandle *ScoreHandle, struct GS_Score *Score, struct GS_ScoreList *Scores, ULONG ScoreType)
  437.  
  438. {
  439.   struct GS_Score *Current;
  440.   struct GS_Score *Pred;
  441.   int Flag;
  442.  
  443.   Flag=FALSE;
  444.   for (Current=(struct GS_Score *)Scores->List.mlh_Head, Pred=NULL;
  445.        Current->Node.mln_Succ!=NULL;
  446.        Pred=Current, Current=(struct GS_Score *)Current->Node.mln_Succ)
  447.     {
  448.       if (ScoreType==GS_SCORE_PERSONAL)
  449.     {
  450.       if ((LONG)CallHookPkt(ScoreHandle->ScoreDef->CompareHook,Score,Current)>0)
  451.         {
  452.           Insert((struct List *)&Scores->List,(struct Node *)&Score->Node,(struct Node *)&Pred->Node);
  453.           Scores->Count++;
  454.           Flag=TRUE;
  455.           break;
  456.         }
  457.     }
  458.       else
  459.     {
  460.       if (Flag)
  461.         {
  462.           if (MyStrcmp(Score->Name,Current->Name)==0)
  463.         {
  464.           Scores->Count--;
  465.           Remove((struct Node *)&Current->Node);
  466.           return Current;
  467.         }
  468.         }
  469.       else
  470.         {
  471.           if ((LONG)CallHookPkt(ScoreHandle->ScoreDef->CompareHook,Score,Current)>0)
  472.         {
  473.           Insert((struct List *)&Scores->List,(struct Node *)&Score->Node,(struct Node *)&Pred->Node);
  474.           Scores->Count++;
  475.           Flag=TRUE;
  476.         }
  477.           else
  478.         {
  479.           if (MyStrcmp(Score->Name,Current->Name)==0)
  480.             {
  481.               return NULL;
  482.             }
  483.         }
  484.         }
  485.     }
  486.     }
  487.   if (!Flag)
  488.     {
  489.       AddTail((struct List *)&Scores->List,(struct Node *)&Score->Node);
  490.       Scores->Count++;
  491.     }
  492.   if (Scores->Count>ScoreHandle->ScoreDef->TableSize[ScoreType])
  493.     {
  494.       Scores->Count--;
  495.       return (struct GS_Score *)RemTail((struct List *)&Scores->List);
  496.     }
  497.   return NULL;
  498. }
  499.  
  500. /************************************************************************/
  501. /*                                    */
  502. /* Check whether a score has expired.                    */
  503. /*                                    */
  504. /************************************************************************/
  505.  
  506. static int CheckExpire(ULONG TimeStamp, const struct DateStamp *Now)
  507.  
  508. {
  509.   ULONG Day, Minute, Second;
  510.  
  511.   Second=TimeStamp;
  512.   Day=Second/(60*60*24);
  513.   Second=Second%(60*60*24);
  514.   Minute=Second/60;
  515.   if (!(Day==Now->ds_Days || (Day==Now->ds_Days-1 && Now->ds_Minute<12*60 && Minute>=21*60)))
  516.     {
  517.       return TRUE;
  518.     }
  519.   return FALSE;
  520. }
  521.  
  522. /************************************************************************/
  523. /*                                    */
  524. /* Read a score table.                            */
  525. /* Returns a DOS (>0) or IFF (<0) error code.                */
  526. /* In case of error, the scores are unchanged.                */
  527. /*                                    */
  528. /************************************************************************/
  529.  
  530. static INLINE LONG ReadScoreTable(struct ScoreHandle *ScoreHandle, ULONG ScoreType, BPTR Filehandle, const struct DateStamp *Now)
  531.  
  532. {
  533.   LONG Error;
  534.   struct Library *IFFParseBase;
  535.  
  536.   Error=0;
  537.   if ((IFFParseBase=OpenLibrary(IFFParseName,36))!=NULL)
  538.     {
  539.       struct IFFHandle *IFFHandle;
  540.  
  541.       if ((IFFHandle=AllocIFF())!=NULL)
  542.     {
  543.       IFFHandle->iff_Stream=(ULONG)Filehandle;
  544.       InitIFFasDOS(IFFHandle);
  545.       if (!(Error=OpenIFF(IFFHandle,IFFF_READ)))
  546.         {
  547.           if (!(Error=PropChunk(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('P','L','Y','R'))) &&
  548.           !(Error=PropChunk(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('T','I','M','E'))) &&
  549.           !(Error=StopOnExit(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('F','O','R','M'))))
  550.         {
  551.           const struct GS_ScoreDef *ScoreDef;
  552.           const struct GS_ScoreChunkDef *ChunkDef;
  553.           LONG i;
  554.  
  555.           ScoreDef=ScoreHandle->ScoreDef;
  556.           for (i=ScoreDef->ChunkCount, ChunkDef=(const struct GS_ScoreChunkDef *)(ScoreDef+1);
  557.                i-->0 && !(Error=PropChunk(IFFHandle,MAKE_ID('S','C','O','R'),ChunkDef->ChunkID));
  558.                ChunkDef++)
  559.             ;
  560.           if (!Error)
  561.             {
  562.               ULONG ScoreSize;
  563.               struct GS_ScoreList Scores;
  564.  
  565.               Scores.Count=0;
  566.               NewList((struct List *)&Scores.List);
  567.               ScoreSize=sizeof(struct GS_Score)+sizeof(ULONG)*ScoreDef->ChunkCount;
  568.               do
  569.             {
  570.               Error=ParseIFF(IFFHandle,IFFPARSE_SCAN);
  571.               if (Error==IFFERR_EOC)
  572.                 {
  573.                   const struct StoredProperty *TimeProp;
  574.  
  575.                   Error=0;
  576.                   TimeProp=FindProp(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('T','I','M','E'));
  577.                   if (TimeProp!=NULL &&
  578.                   TimeProp->sp_Size==sizeof(ULONG) &&
  579.                   (ScoreType!=GS_SCORE_TODAY || !CheckExpire(*(ULONG *)TimeProp->sp_Data,Now)))
  580.                 {
  581.                   struct GS_Score *Score;
  582.                   ULONG Size;
  583.  
  584.                   Size=ScoreSize;
  585.  
  586.                   for (i=0, ChunkDef=(const struct GS_ScoreChunkDef *)(ScoreDef+1);
  587.                        !Error && i<ScoreDef->ChunkCount;
  588.                        i++, ChunkDef++)
  589.                     {
  590.                       struct StoredProperty *Prop;
  591.  
  592.                       Prop=FindProp(IFFHandle,MAKE_ID('S','C','O','R'),ChunkDef->ChunkID);
  593.                       if (Prop!=NULL)
  594.                     {
  595.                       if (ChunkDef->Flags & GS_SCOREDEFF_INTEGER)
  596.                         {
  597.                           if (Prop->sp_Size!=sizeof(ULONG))
  598.                         {
  599.                           continue;
  600.                         }
  601.                         }
  602.                     }
  603.                       else
  604.                     {
  605.                       continue;
  606.                     }
  607.                     }
  608.                   if (!Error)
  609.                     {
  610.                       const struct StoredProperty *NameProp;
  611.  
  612.                       NameProp=FindProp(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('P','L','Y','R'));
  613.                       if (NameProp!=NULL && NameProp->sp_Size!=0)
  614.                     {
  615.                       Size+=NameProp->sp_Size+1;
  616.                     }
  617.                       if ((Score=GS_MemoryAlloc(Size))!=NULL)
  618.                     {
  619.                       char *t;
  620.  
  621.                       t=((char *)Score)+sizeof(*Score)+sizeof(ULONG)*ScoreDef->ChunkCount;
  622.                       Score->TimeStamp=*(ULONG *)TimeProp->sp_Data;
  623.  
  624.                       for (i=0, ChunkDef=(const struct GS_ScoreChunkDef *)(ScoreDef+1);
  625.                            i<ScoreDef->ChunkCount;
  626.                            i++, ChunkDef++)
  627.                         {
  628.                           struct StoredProperty *Prop;
  629.  
  630.                           Prop=FindProp(IFFHandle,MAKE_ID('S','C','O','R'),ChunkDef->ChunkID);
  631.                           if (ChunkDef->Flags & GS_SCOREDEFF_INTEGER)
  632.                         {
  633.                           ((ULONG *)(Score+1))[i]=*(ULONG *)Prop->sp_Data;
  634.                         }
  635.                         }
  636.                       if (NameProp!=NULL && NameProp->sp_Size!=0)
  637.                         {
  638.                           Score->Name=t;
  639.                           CopyMem(NameProp->sp_Data,t,NameProp->sp_Size);
  640.                           t[NameProp->sp_Size]='\0';
  641.                         }
  642.                       else
  643.                         {
  644.                           Score->Name=NULL;
  645.                         }
  646.                       GS_MemoryFree(InsertScore(ScoreHandle,Score,&Scores,ScoreType));
  647.                     }
  648.                       else
  649.                     {
  650.                       Error=ERROR_NO_FREE_STORE;
  651.                     }
  652.                     }
  653.                 }
  654.                 }
  655.             }
  656.               while (!Error);
  657.               if (Error==IFFERR_NOMEM)
  658.             {
  659.               Error=ERROR_NO_FREE_STORE;
  660.             }
  661.               else if (Error==IFFERR_EOF)
  662.             {
  663.               Error=0;
  664.             }
  665.               if (Error!=0)
  666.             {
  667.               FreeScores(&Scores);
  668.             }
  669.               else
  670.             {
  671.               struct GS_Score *Score;
  672.  
  673.               FreeScores(&ScoreHandle->FileData[ScoreType].Scores);
  674.               while ((Score=(struct GS_Score *)RemHead((struct List *)&Scores.List))!=NULL)
  675.                 {
  676.                   AddTail((struct List *)&ScoreHandle->FileData[ScoreType].Scores.List,
  677.                       (struct Node *)&Score->Node);
  678.                   ScoreHandle->FileData[ScoreType].Scores.Count++;
  679.                 }
  680.               assert(ScoreHandle->FileData[ScoreType].Scores.Count==Scores.Count);
  681.             }
  682.             }
  683.         }
  684.           CloseIFF(IFFHandle);
  685.         }
  686.       FreeIFF(IFFHandle);
  687.     }
  688.       CloseLibrary(IFFParseBase);
  689.     }
  690.   return Error;
  691. }
  692.  
  693. /************************************************************************/
  694. /*                                    */
  695. /* Update the score list.                        */
  696. /*                                    */
  697. /************************************************************************/
  698.  
  699. static LONG UpdateScores(struct ScoreHandle *ScoreHandle, ULONG ScoreType, int Force)
  700.  
  701. {
  702.   LONG Error;
  703.   char *Filename;
  704.  
  705.   Error=0;
  706.   if ((Filename=ScoreHandle->FileData[ScoreType].Filename)!=NULL)
  707.     {
  708.       BPTR OldCurrentDir;
  709.       BPTR FileLock;
  710.       struct DateStamp Now;
  711.  
  712.       DateStamp(&Now);
  713.       OldCurrentDir=CurrentDir(ScoreHandle->DirLock);
  714.       if (Force)
  715.     {
  716.       while ((FileLock=Lock(Filename,SHARED_LOCK))==MKBADDR(NULL) && (Error=IoErr())==ERROR_OBJECT_IN_USE)
  717.         {
  718.           Delay(TICKS_PER_SECOND);
  719.         }
  720.     }
  721.       else
  722.     {
  723.       if ((FileLock=Lock(Filename,SHARED_LOCK))==MKBADDR(NULL))
  724.         {
  725.           Error=IoErr();
  726.         }
  727.     }
  728.       CurrentDir(OldCurrentDir);
  729.       if (FileLock!=MKBADDR(NULL))
  730.     {
  731.       struct FileInfoBlock ALIGN(FileInfoBlock);
  732.  
  733.       Error=0;
  734.       if (Examine(FileLock,&FileInfoBlock))
  735.         {
  736.           if (CompareDates(&FileInfoBlock.fib_Date,&ScoreHandle->FileData[ScoreType].LastChanged))
  737.         {
  738.           BPTR File;
  739.  
  740.           if ((File=OpenFromLock(FileLock))!=MKBADDR(NULL))
  741.             {
  742.               FileLock=MKBADDR(NULL);
  743.               if ((Error=ReadScoreTable(ScoreHandle,ScoreType,File,&Now))==0)
  744.             {
  745.               ScoreHandle->FileData[ScoreType].LastChanged=FileInfoBlock.fib_Date;
  746.             }
  747.               Close(File);
  748.             }
  749.           else
  750.             {
  751.               Error=IoErr();
  752.             }
  753.         }
  754.         }
  755.       else
  756.         {
  757.           Error=IoErr();
  758.         }
  759.       if (FileLock!=MKBADDR(NULL))
  760.         {
  761.           UnLock(FileLock);
  762.         }
  763.     }
  764.       else
  765.     {
  766.       if (Error==ERROR_OBJECT_NOT_FOUND)
  767.         {
  768.           FreeScores(&ScoreHandle->FileData[ScoreType].Scores);
  769.           Error=0;
  770.         }
  771.     }
  772.       if (ScoreType==GS_SCORE_TODAY)
  773.     {
  774.       struct GS_Score *Score, *Next;
  775.  
  776.       for (Score=(struct GS_Score *)ScoreHandle->FileData[ScoreType].Scores.List.mlh_Head;
  777.            (Next=(struct GS_Score *)Score->Node.mln_Succ)!=NULL;
  778.            Score=Next)
  779.         {
  780.           if (CheckExpire(Score->TimeStamp,&Now))
  781.         {
  782.           Remove((struct Node *)&Score->Node);
  783.           GS_MemoryFree(Score);
  784.         }
  785.         }
  786.     }
  787.     }
  788.   return Error;
  789. }
  790.  
  791. /****** gamesupport.library/GS_ObtainScores ******************************
  792. *
  793. *    NAME
  794. *    GS_ObtainScores -- obtain a score table
  795. *
  796. *    SYNOPSIS
  797. *    Scores = GS_ObtainScores(ScoreHandle,ScoreType)
  798. *      d0                        a0         d0
  799. *
  800. *    const GS_ScoreList *GS_ObtainScores(void *, ULONG);
  801. *
  802. *    FUNCTION
  803. *    Return a score table.
  804. *
  805. *    INPUTS
  806. *    ScoreHandle  - a handle describing the files
  807. *    ScoreType    - the file type that we want to read
  808. *
  809. *    RESULT
  810. *    Scores - a (read-only) linked list of scores. NULL for no
  811. *             scores.
  812. *
  813. *    NOTE
  814. *    This function cannot fail: if we can't read the new scores,
  815. *    the old scores are returned instead. This is because the
  816. *    only thing we can do with the scores is to display them,
  817. *    and it's better to display old scores instead of no scores
  818. *    at all.
  819. *
  820. *    SEE ALSO
  821. *    GS_ObtainScoreHandle(), GS_ReleaseScores()
  822. *
  823. *************************************************************************/
  824.  
  825. SAVEDS_ASM_D0A0(struct GS_ScoreList *,LibGS_ObtainScores,ULONG,ScoreType,struct ScoreHandle *,ScoreHandle)
  826.  
  827. {
  828.   struct GS_ScoreList *Scores;
  829.  
  830.   if (ScoreHandle!=NULL)
  831.     {
  832.       SetIoErr(UpdateScores(ScoreHandle,ScoreType,FALSE));
  833.       Scores=&ScoreHandle->FileData[ScoreType].Scores;
  834.       if (Scores->Count==0)
  835.     {
  836.       assert(IsListEmpty((struct List *)&Scores->List));
  837.       Scores=NULL;
  838.     }
  839.     }
  840.   else
  841.     {
  842.       Scores=NULL;
  843.     }
  844.   return Scores;
  845. }
  846.  
  847. /****** gamesupport.library/GS_ReleaseScores *****************************
  848. *
  849. *    NAME
  850. *    GS_ReleaseScores -- release a score table
  851. *
  852. *    SYNOPSIS
  853. *    GS_ReleaseScores(ScoreHandle, Scores)
  854. *                       a0           a1
  855. *
  856. *************************************************************************/
  857.  
  858. SAVEDS_ASM_A0A1(void,LibGS_ReleaseScores,struct ScoreHandle *,ScoreHandle,struct GS_ScoreList *,Scores)
  859.  
  860. {
  861. }
  862.  
  863. /************************************************************************/
  864. /*                                    */
  865. /* Write a score file                            */
  866. /*                                    */
  867. /************************************************************************/
  868.  
  869. static INLINE LONG WriteScoreTable(const struct ScoreHandle *ScoreHandle, ULONG ScoreType)
  870.  
  871. {
  872.   BPTR File;
  873.   LONG Error;
  874.  
  875.   Error=0;
  876.   if ((File=Open(TempFilename,MODE_NEWFILE))!=MKBADDR(NULL))
  877.     {
  878.       struct Library *IFFParseBase;
  879.  
  880.       if ((IFFParseBase=OpenLibrary(IFFParseName,36))!=NULL)
  881.     {
  882.       struct IFFHandle *IFFHandle;
  883.  
  884.       if ((IFFHandle=AllocIFF()))
  885.         {
  886.           LONG Error;
  887.  
  888.           IFFHandle->iff_Stream=File;
  889.           InitIFFasDOS(IFFHandle);
  890.           if (!(Error=OpenIFF(IFFHandle,IFFF_WRITE)))
  891.         {
  892.           if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('L','I','S','T'),IFFSIZE_UNKNOWN)))
  893.             {
  894.               if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),
  895.                         MAKE_ID('P','R','O','P'),IFFSIZE_UNKNOWN)))
  896.             {
  897.               ULONG Length;
  898.  
  899.               if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('G','A','M','E'),Length=strlen(ScoreHandle->ScoreDef->GameName))) &&
  900.                   !(Error=MyWriteChunkBytes(IFFParseBase,IFFHandle,ScoreHandle->ScoreDef->GameName,Length)) &&
  901.                   !(Error=PopChunk(IFFHandle)))
  902.                 {
  903.                   const struct GS_Score *Score;
  904.  
  905.                   if (ScoreType==GS_SCORE_PERSONAL)
  906.                 {
  907.                   if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),MAKE_ID('P','L','Y','R'),Length=strlen(ScoreHandle->UserName))) &&
  908.                       !(Error=MyWriteChunkBytes(IFFParseBase,IFFHandle,ScoreHandle->UserName,Length)))
  909.                     {
  910.                       Error=PopChunk(IFFHandle);
  911.                     }
  912.                 }
  913.                   if (Error==0)
  914.                 {
  915.                   Error=PopChunk(IFFHandle);    /* PROP SCOR */
  916.                 }
  917.                   Score=(struct GS_Score *)ScoreHandle->FileData[ScoreType].Scores.List.mlh_Head;
  918.                   while (!Error && Score->Node.mln_Succ!=NULL)
  919.                 {
  920.                   if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),
  921.                             MAKE_ID('F','O','R','M'),IFFSIZE_UNKNOWN)))
  922.                     {
  923.                       LONG j;
  924.  
  925.                       for (j=-2; !Error && j<ScoreHandle->ScoreDef->ChunkCount; j++)
  926.                     {
  927.                       const void *Data;
  928.                       ULONG Size;
  929.                       ULONG ChunkID;
  930.  
  931.                       Data=NULL;
  932.                       if (j==-2)
  933.                         {
  934.                           /* player name */
  935.                           if (ScoreType!=GS_SCORE_PERSONAL)
  936.                         {
  937.                           if (Score->Name!=NULL)
  938.                             {
  939.                               ChunkID=MAKE_ID('P','L','Y','R');
  940.                               Data=Score->Name;
  941.                               Size=strlen(Data);
  942.                             }
  943.                         }
  944.                         }
  945.                       else if (j==-1)
  946.                         {
  947.                           /* timestamp */
  948.                           ChunkID=MAKE_ID('T','I','M','E');
  949.                           Data=&Score->TimeStamp;
  950.                           Size=sizeof(Score->TimeStamp);
  951.                         }
  952.                       else
  953.                         {
  954.                           const struct GS_ScoreChunkDef *ChunkDef;
  955.  
  956.                           ChunkDef=((const struct GS_ScoreChunkDef *)(ScoreHandle->ScoreDef+1))+j;
  957.                           ChunkID=ChunkDef->ChunkID;
  958.                           if (ChunkDef->Flags & GS_SCOREDEFF_INTEGER)
  959.                         {
  960.                           Data=&((const ULONG *)(Score+1))[j];
  961.                           Size=sizeof(ULONG);
  962.                         }
  963.                         }
  964.                       if (Data!=NULL)
  965.                         {
  966.                           if (!(Error=PushChunk(IFFHandle,MAKE_ID('S','C','O','R'),ChunkID,Size)) &&
  967.                           !(Error=MyWriteChunkBytes(IFFParseBase,IFFHandle,Data,Size)))
  968.                         {
  969.                           Error=PopChunk(IFFHandle);
  970.                         }
  971.                         }
  972.                     }
  973.                       if (!Error)
  974.                     {
  975.                       Error=PopChunk(IFFHandle);    /* FORM SCOR */
  976.                     }
  977.                     }
  978.                   Score=(struct GS_Score *)Score->Node.mln_Succ;
  979.                 }
  980.                 }
  981.             }
  982.               if (!Error)
  983.             {
  984.               Error=PopChunk(IFFHandle);    /* LIST SCOR */
  985.             }
  986.             }
  987.           CloseIFF(IFFHandle);
  988.         }
  989.           FreeIFF(IFFHandle);
  990.         }
  991.       else
  992.         {
  993.           Error=ERROR_NO_FREE_STORE;
  994.         }
  995.       CloseLibrary(IFFParseBase);
  996.     }
  997.       else
  998.     {
  999.       Error=-1;
  1000.     }
  1001.       if (!Close(File) && Error==0)
  1002.     {
  1003.       Error=IoErr();
  1004.     }
  1005.       if (Error==0)
  1006.     {
  1007.       SetProtection(TempFilename,FIBF_EXECUTE);
  1008.     }
  1009.       else
  1010.     {
  1011.       DeleteFile(TempFilename);
  1012.     }
  1013.     }
  1014.   else
  1015.     {
  1016.       Error=IoErr();
  1017.     }
  1018.   return Error;
  1019. }
  1020.  
  1021. /************************************************************************/
  1022. /*                                    */
  1023. /* Copy a score                                */
  1024. /*                                    */
  1025. /************************************************************************/
  1026.  
  1027. static INLINE struct GS_Score *CopyScore(const struct ScoreHandle *ScoreHandle, const struct GS_Score *Score)
  1028.  
  1029. {
  1030.   struct GS_Score *NewScore;
  1031.   ULONG NameSize;
  1032.   ULONG ScoreSize;
  1033.  
  1034.   ScoreSize=sizeof(*NewScore)+sizeof(ULONG)*ScoreHandle->ScoreDef->ChunkCount;
  1035.   if (Score->Name!=NULL)
  1036.     {
  1037.       NameSize=strlen(Score->Name)+1;
  1038.     }
  1039.   else
  1040.     {
  1041.       NameSize=0;
  1042.     }
  1043.   if ((NewScore=GS_MemoryAlloc(ScoreSize+NameSize))!=NULL)
  1044.     {
  1045.       CopyMem((APTR)Score,NewScore,ScoreSize);
  1046.       if (NameSize!=0)
  1047.     {
  1048.       NewScore->Name=((char *)NewScore)+ScoreSize;
  1049.       Stpcpy(((char *)NewScore)+ScoreSize,Score->Name);
  1050.     }
  1051.       else
  1052.     {
  1053.       NewScore->Name=NULL;
  1054.     }
  1055.     }
  1056.   return NewScore;
  1057. }
  1058.  
  1059. /****** gamesupport.library/GS_InsertScore *******************************
  1060. *
  1061. *   NAME
  1062. *    GS_InsertScore -- insert a score into the score tables
  1063. *
  1064. *   SYNOPSIS
  1065. *    Error = GS_InsertScore(ScoreHandle, Score)
  1066. *     d0                        a0         a1
  1067. *
  1068. *    LONG GS_InsertScore(void *, struct GS_Score *);
  1069. *
  1070. *   FUNCTION
  1071. *    Check if we want to insert the new score. If yes, insert
  1072. *    it. Basically, you are expected to call this function
  1073. *    whenever a game is finished --- we determine ourselves
  1074. *    whether we actually want to insert the score.
  1075. *
  1076. *   INPUTS
  1077. *    ScoreHandle  - a handle describing the files
  1078. *    Score        - the new score to insert
  1079. *
  1080. *   RESULT
  1081. *    Error - error code (0 for success)
  1082. *            In any case, the Score->Name and Score->TimeStamp
  1083. *            will be set.
  1084. *
  1085. *************************************************************************/
  1086.  
  1087. SAVEDS_ASM_A0A1(LONG,LibGS_InsertScore,struct ScoreHandle *,ScoreHandle,struct GS_Score *,Score)
  1088.  
  1089. {
  1090.   BPTR OldCurrentDir;
  1091.   LONG Error;
  1092.   BPTR LockFile;
  1093.  
  1094.   Error=0;
  1095.   Score->Name=ScoreHandle->UserName;
  1096.   {
  1097.     ULONG Micros;
  1098.  
  1099.     CurrentTime(&Score->TimeStamp,&Micros);
  1100.   }
  1101.   OldCurrentDir=CurrentDir(ScoreHandle->DirLock);
  1102.   while ((LockFile=Open(LockFilename,MODE_NEWFILE))==MKBADDR(NULL) && 
  1103.      ((Error=IoErr())==ERROR_OBJECT_EXISTS || Error==ERROR_OBJECT_IN_USE))
  1104.     {
  1105.       Delay(TICKS_PER_SECOND);
  1106.     }
  1107.   if (LockFile!=MKBADDR(NULL))
  1108.     {
  1109.       ULONG i;
  1110.  
  1111.       Error=0;
  1112.       for (i=0; Error==0 && i<3; i++)
  1113.     {
  1114.       Error=UpdateScores(ScoreHandle,i,TRUE);
  1115.     }
  1116.       for (i=0; Error==0 && i<3; i++)
  1117.     {
  1118.       struct GS_Score *NewScore;
  1119.  
  1120.       if ((NewScore=CopyScore(ScoreHandle,Score))!=NULL)
  1121.         {
  1122.           struct GS_ScoreList *Scores;
  1123.           struct GS_Score *TrashScore;
  1124.  
  1125.           Scores=&ScoreHandle->FileData[i].Scores;
  1126.           TrashScore=InsertScore(ScoreHandle,NewScore,Scores,i);
  1127.           GS_MemoryFree(TrashScore);
  1128.           if (TrashScore!=NewScore)
  1129.         {
  1130.           if ((Error=WriteScoreTable(ScoreHandle,i))==0)
  1131.             {
  1132.               do
  1133.             {
  1134.               if (DeleteFile(ScoreHandle->FileData[i].Filename) ||
  1135.                   (Error=IoErr())==ERROR_OBJECT_NOT_FOUND)
  1136.                 {
  1137.                   Error=0;
  1138.                 }
  1139.             }
  1140.               while (Error==0 && !Rename(TempFilename,ScoreHandle->FileData[i].Filename));
  1141.               if (Error!=0)
  1142.             {
  1143.               DeleteFile(TempFilename);
  1144.             }
  1145.             }
  1146.           if (Error!=0)
  1147.             {
  1148.               ScoreHandle->FileData[i].LastChanged.ds_Days=0;
  1149.               ScoreHandle->FileData[i].LastChanged.ds_Minute=0;
  1150.               ScoreHandle->FileData[i].LastChanged.ds_Tick=0;
  1151.             }
  1152.         }
  1153.         }
  1154.       else
  1155.         {
  1156.           Error=ERROR_NO_FREE_STORE;
  1157.         }
  1158.     }
  1159.       Close(LockFile);
  1160.       DeleteFile(LockFilename);
  1161.     }
  1162.   CurrentDir(OldCurrentDir);
  1163.   return Error;
  1164. }
  1165.